﻿using HtmlAgilityPack;
using Newtonsoft.Json;
using Serilog;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using XjjXmm.FrameWork.Cache;
using XjjXmm.Spider.biquge;

namespace XjjXmm.Spider
{
    internal class NovelSpider
    {
        //https://www.zhihu.com/question/359676590/answer/1615531107


        private readonly ISqlSugarClient _sqlSugarClient;
        private readonly ISiteSpider _spider;

        public NovelSpider(ISqlSugarClient sqlSugarClient, ISiteSpider spider)
        {
            _sqlSugarClient = sqlSugarClient;
            _spider = spider;
        }

        public async Task test()
        {
            await foreach (var link in _spider.FetchNovelLinks())
            {
                Console.WriteLine(link);
            }
            var a = "";
        }
        private async Task Retry(int maxRetryCount, Func<Task> funcGetData)
        {
            Exception lastException = null;
            var fixdMaxRetryCount = maxRetryCount > 1 ? maxRetryCount : 1;
            for (var i = 0; i < fixdMaxRetryCount; i++)
            {
                try
                {
                    await funcGetData();

                }
                catch (Exception e)
                {
                    lastException = e;
                    _spider.GetLogger().Error(e, _spider.Domain);

                    await Task.Delay(TimeSpan.FromSeconds(60 * maxRetryCount));
                }

                if (lastException == null)
                {
                    break;
                }
            }
            if (lastException != null)
            {
                throw lastException;
            }
        }

        /// <summary>
        /// 抓取未入库的小说， 基本信息
        /// </summary>
        /// <returns></returns>
        private async Task ImportUnFetchLink()
        {
            await Retry(5, async () =>
            {
                await foreach (var link in _spider.FetchNovelLinks())
                {                                           
                    // 判断小说是否抓取过
                    var isExistNovel = await _sqlSugarClient.Queryable<NovelEntity>()
                     .Where(t => t.Link == link && t.Domain == _spider.Domain)
                     .AnyAsync();

                    if (!isExistNovel)
                    {
                        try
                        {
                            var novel = await _spider.FetchNovel(link);
                            var novelEntity = new NovelEntity()
                            {
                                Title = novel.Title,
                                Author = novel.Author,
                                Category = novel.Category,
                                IsInstalment = novel.IsInstalment,
                                LastChapter = novel.LastChapter,
                                UpdateTime = novel.UpdateTime,
                                Intro = novel.Intro,

                                Link = link,
                                Domain = _spider.Domain,
                                FetchTime = DateTime.Now,
                                Status = 0
                            };

                            _spider.GetLogger().Information($"新增小说： {novel.Title}: {_spider.Domain}");

                            await _sqlSugarClient.Insertable(novelEntity).ExecuteCommandAsync();
                        }
                        catch (Exception ex)
                        {
                            _spider.GetLogger().Error(ex, $"跳过小说： {link}: {_spider.Domain}");
                        }
                    }
                }

            });

            _spider.GetLogger().Information($"小说文章抓取完毕：  {_spider.Domain}");
            //await GetDetail("/15/15021/6803437.html");
        }

        private async Task ImportChapters()
        {
            await Retry(5, async () =>
            {
                var novelList = await _sqlSugarClient.Queryable<NovelEntity>().Where(t => (t.Status == 0 || t.Status == 1) && t.Domain == _spider.Domain).ToListAsync();

                foreach (var novel in novelList)
                {
                    //查询公用池子是否存在
                    var novelPool = await _sqlSugarClient.Queryable<NovelPoolEntity>().Where(t => t.Title == novel.Title && t.Author == novel.Author).SingleAsync();
                    if (novelPool?.IsFinished == true)
                    {
                        if (novelPool?.Domain == novel.Domain)
                        {
                            novel.Status = 2;

                        }
                        else
                        {
                            novel.Status = 3;
                        }

                        await _sqlSugarClient.Updateable(novel).ExecuteCommandAsync();

                        continue;
                    }

                    if (novelPool != null && novelPool?.Domain != novel.Domain)
                    {
                        continue;
                    }

                    int id = 0;
                    if (novelPool == null)
                    {
                        novelPool = new NovelPoolEntity()
                        {
                            Title = novel.Title,
                            Author = novel.Author,
                            Link = novel.Link,
                            Domain = novel.Domain,
                            IsFinished = false
                        };

                        id = await _sqlSugarClient.Insertable(novelPool).ExecuteReturnIdentityAsync();
                    }
                    else
                    {
                        id = novelPool.Id;
                    }

                    var existsChapters = await _sqlSugarClient.Queryable<Chapter>().Where(t => t.NovelId == id).ToListAsync();


                    await foreach (var chapter in _spider.FetchChapters(novelPool.Link))
                    {
                        if (existsChapters.Exists(t => t.Title == chapter.Title))
                        {
                            continue;
                        }

                        chapter.NovelId = id;

                        try
                        {
                            var content = await _spider.FetchContent(chapter.Link);
                            chapter.Content = content;
                            await _sqlSugarClient.Insertable<Chapter>(chapter).ExecuteCommandAsync();
                        }
                        catch (Exception ex)
                        {
                            // _spider.GetLogger().Information("FetchContent: {}, chapter:{}", chapter.Link, JsonConvert.SerializeObject(chapter));
                            _spider.GetLogger().Error(ex, $"GetChaptersDetailURL:{chapter.Link}, chapter: {JsonConvert.SerializeObject(chapter)}");
                            throw;
                        }


                        _spider.GetLogger().Information($"新增章节： {chapter.Title}: {_spider.Domain}");

                    }

                    novelPool.Id = id;
                    novelPool.IsFinished = true;
                    await _sqlSugarClient.Updateable(novelPool).ExecuteCommandAsync();

                    novel.Status = 2;
                    await _sqlSugarClient.Updateable(novel).ExecuteCommandAsync();
                }
            });
        }

        public async Task Run()
        {
           // test();

            //return;

            Task t1 = ImportUnFetchLink();

            await Task.Delay(5000);

            Task t2 = ImportChapters();

            while (t2.IsCompleted && !t1.IsCompleted)
            {
                t2 = ImportChapters();
                _spider.GetLogger().Information($"{_spider.Domain}: 抓取列表为完成，抓取章节task已经完成，等待新列表");
                await Task.Delay(5000);
            }

            Task.WaitAll(t1, t2);

            _spider.GetLogger().Information("抓取结束");
        }



        private async Task GetArticle(string? url)
        {
            /* try
             {
                 var link = url?.Replace(Domain, "");

                 if (string.IsNullOrEmpty(link))
                 {
                     throw new ArgumentNullException("url");
                 }

                 var existNovel = await _sqlSugarClient.Queryable<NovelEntity>()
                  .Where(t => t.Link == url && t.Domain == Domain)
                  .FirstAsync();

                 if (existNovel?.IsFinished == true)
                 {
                     return;
                 }

                 await Task.Delay(2000);


                 //var htmlDoc = GetNovel(link);

                 var novel = await GetNovel(link);
                 novel.Link = url;

                 bool hasAlreadyImport = await _sqlSugarClient.Queryable<NovelEntity>()
                      .Where(t => t.Title == novel.Title && t.Author == novel.Author && t.Domain != Domain)
                      .AnyAsync();

                 if (hasAlreadyImport)
                 {
                     return;
                 }

                 var id = 0;
                 if (existNovel == null)
                 {
                     id = await _sqlSugarClient.Insertable(novel).ExecuteReturnIdentityAsync();
                 }
                 else
                 {
                     id = existNovel.Id;
                 }
                 //_sqlSugarClient.Saveable<Novel>(novel);
                 //var charapters = GetChapters(htmlDoc);

                 Logger.Debug($"小说: {novel.Title}, 作者: {novel.Author}");
                 foreach (var chapter in novel.Chapters)
                 {
                     //var title = chapter.title;

                     //var author = chapter.author;

                     //var chapterTitle = chapter.chapterTitle;

                     Logger.Debug($"小说: {novel.Title}, 章节：{chapter.Title}");

                     chapter.NovelId = id;


                     var isChapterExist = await _sqlSugarClient.Queryable<Chapter>().Where(t => t.Title == chapter.Title && t.NovelId == id).AnyAsync();
                     if (isChapterExist)
                     {
                         continue;
                     }

                     try
                     {
                         var content = await GetChaptersDetail(chapter.Link);
                         chapter.Content = content;
                         await _sqlSugarClient.Insertable<Chapter>(chapter).ExecuteCommandAsync();
                     }
                     catch (Exception ex)
                     {
                         Logger.Information("chapter:{}", JsonConvert.SerializeObject(chapter));
                         Logger.Error(ex, "GetChaptersDetail");
                         throw;
                     }



                     await Task.Delay(5000);
                 }

                 novel.Id = id;
                 novel.IsFinished = true;

                 await _sqlSugarClient.Updateable(novel).ExecuteCommandAsync();
             }
             catch (Exception ex)
             {
                 Logger.Error(ex, "GetArticle");
                 throw;
             }*/

        }



        //protected abstract string Domain { get; }

        //protected abstract IAsyncEnumerable<string> FetchNovelLinks();

        //protected abstract Task<NovelModel> FetchNovel(string url);

        //protected abstract IAsyncEnumerable<Chapter> FetchChapters(string url);

        //protected abstract Task<string> FetchContent(string url);
    }

    public class ClientBuildHelper<T>
    {
        private List<HttpClient> _clients = new List<HttpClient>();
        private readonly IHttpClientFactory httpClientFactory;

        public HttpClient Client
        {
            get
            {
                var random = new Random();
                var index = random.Next(_clients.Count);
                return _clients[index];
            }
        }

        public ClientBuildHelper(IHttpClientFactory httpClientFactory)
        {
            this.httpClientFactory = httpClientFactory;
        }


        public void Build()
        {
            var keys = Setting.GetKeys<T>();

            keys.ForEach(key => _clients.Add(httpClientFactory.CreateClient(key)));

        }
    }
}
